﻿// -----------------------------------------------------------------------
// <copyright file="PushRequestService.cs" company="Zen Design Corp">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Zen.BlackBerry.Push.Request
{
	using System;
	using System.Collections.Concurrent;
	using System.Collections.Concurrent.Partitioners;
	using System.Collections.Generic;
	using Microsoft.Practices.Unity;
	using Zen.BlackBerry.Common;
	using Zen.BlackBerry.Configuration;
	using Zen.BlackBerry.Pap;
	using Zen.BlackBerry.Push.AppManagement;
	using Zen.BlackBerry.Subscription;

	/// <summary>
	/// Implementation of the <see cref="IPushRequestService"/> interface.
	/// </summary>
	public class PushRequestService : IPushRequestService
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="PushRequestService"/> class.
		/// </summary>
		public PushRequestService()
		{
		}

		/// <summary>
		/// Gets or sets the push properties.
		/// </summary>
		/// <value>The push properties.</value>
		[Dependency]
		public IPushProperties PushProperties
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the repository.
		/// </summary>
		/// <value>The repository.</value>
		[Dependency]
		public IPushRequestRepository Repository
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the application service.
		/// </summary>
		/// <value>The application service.</value>
		[Dependency]
		public IPushApplicationService ApplicationService
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the request detail service.
		/// </summary>
		/// <value>The request detail service.</value>
		[Dependency]
		public IPushRequestDetailService RequestDetailService
		{
			get;
			set;
		}

		private List<PushRequestDetail> GetPushRequestDetails(PushRequest pushRequest)
		{
			List<PushRequestDetail> pushRequestDetails = new List<PushRequestDetail>();

			foreach (SubscriberPartial subscriber in pushRequest.Subscribers)
			{
				PushRequestDetail pushRequestDetail =
					new PushRequestDetail
					{
						Id = pushRequest.Id,
						CompletedDate = pushRequest.CreatedDate,
						Address = subscriber.Address,
						SubscriberId = subscriber.SubscriberId
					};

				if (subscriber.Type == SubscriberType.Public)
				{
					pushRequestDetail.StatusCode = pushRequest.PublicStatusCode;
					pushRequestDetail.StatusDescription = pushRequest.PublicStatusDescription;
					pushRequestDetail.SubscriberType = SubscriberType.Public;
					if (pushRequest.PublicStatusCode.Code.StartsWith("1"))
					{
						pushRequestDetail.MessageState = MessageState.Pending;
					}
					else
					{
						pushRequestDetail.MessageState = MessageState.Rejected;
					}
				}
				else
				{
					pushRequestDetail.StatusCode = pushRequest.EnterpriseStatusCode;
					pushRequestDetail.StatusDescription = pushRequest.EnterpriseStatusDescription;
					pushRequestDetail.SubscriberType = SubscriberType.Enterprise;
					if (pushRequest.EnterpriseStatusCode.Code.StartsWith("1"))
					{
						pushRequestDetail.MessageState = MessageState.Pending;
					}
					else
					{
						pushRequestDetail.MessageState = MessageState.Rejected;
					}
				}

				pushRequestDetails.Add(pushRequestDetail);
			}

			return pushRequestDetails;
		}

		private void DeletePushRequests(string pushApplicationId, DateTime? toDate, bool completed, string deletedBy)
		{
			if (string.IsNullOrEmpty(deletedBy))
			{
				throw new ArgumentNullException("deletedBy");
			}

			try
			{
				if ((pushApplicationId != null) && (toDate != null))
				{
					Repository.DeletePushRequestsByPushAppIdAndDate(pushApplicationId, toDate.Value, completed, deletedBy);
				}
				else if (pushApplicationId != null)
				{
					Repository.DeletePushRequestsByPushAppId(pushApplicationId, completed, deletedBy);
				}
				else if (toDate != null)
				{
					Repository.DeletePushRequestsByDate(toDate.Value, completed, deletedBy);
				}
				else
				{
					Repository.DeletePushRequests(completed, deletedBy);
				}
			}
			catch (Exception exception)
			{
				//log.error("DataAccessException caught: ", e);
				throw new PushSDKException(string.Format(
					"Exception caught while deleting push requests, pushApplicationId: {0}, toDate: {1}.",
					pushApplicationId, toDate),
					exception);
			}
		}

		#region IPushRequestService Members

		/// <summary>
		/// Adds the specified push request to the persistent store.
		/// </summary>
		/// <param name="pushRequest">The push request.</param>
		public void AddPushRequest(PushRequest pushRequest)
		{
			try
			{
				Repository.AddPushRequest(pushRequest);

				var pushRequestDetails = GetPushRequestDetails(pushRequest);
				if (pushRequest.AddressCount > 1)
				{
					RequestDetailService.AddPushRequestDetails(pushRequestDetails);
				}
				else
				{
					RequestDetailService.AddPushRequestDetail(pushRequestDetails[0]);
				}
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Deletes completed push requests for the specified push application
		/// that are older than the provided date.
		/// </summary>
		/// <param name="pushApplicationId">The push application id.</param>
		/// <param name="toDate">To date.</param>
		/// <param name="deletedBy">The deleted by.</param>
		public void DeleteCompletedPushRequests(string pushApplicationId, DateTime toDate, string deletedBy)
		{
			try
			{
				DeletePushRequests(pushApplicationId, toDate, true, deletedBy);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Deletes a push request with the given push id.
		/// </summary>
		/// <param name="pushId">The push id.</param>
		public void DeletePushRequest(string pushId)
		{
			try
			{
				Repository.DeletePushRequest(pushId);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Deletes push requests with the given push ids.
		/// </summary>
		/// <param name="pushIds">The push ids.</param>
		public void DeletePushRequests(List<string> pushIds)
		{
			try
			{
				if (pushIds.Count < PushProperties.MaxInClauseValues)
				{
					Repository.DeletePushRequests(pushIds);
				}
				else
				{
					// Create a partitioner
					OrderablePartitioner<string> part = ChunkPartitioner.Create<string>(
						pushIds, PushProperties.MaxInClauseValues);

					// Walk the list of partitions and make repeated calls to
					//	delete each block of push-ids.
					int partCount = pushIds.Count / PushProperties.MaxInClauseValues;
					if ((pushIds.Count % PushProperties.MaxInClauseValues) != 0)
					{
						++partCount;
					}

					foreach (var block in part.GetPartitions(partCount))
					{
						EnumerableWrapper<string> wrappedBlock =
							new EnumerableWrapper<string>(block);
						Repository.DeletePushRequests(wrappedBlock);
					}
				}
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Deletes push requests for the specified push application that are
		/// older than the provided date.
		/// </summary>
		/// <param name="pushApplicationId">The push application id.</param>
		/// <param name="toDate">To date.</param>
		/// <param name="deletedBy">The deleted by.</param>
		public void DeletePushRequests(string pushApplicationId, DateTime toDate, string deletedBy)
		{
			DeletePushRequests(pushApplicationId, toDate, false, deletedBy);
		}

		/// <summary>
		/// Finds all push requests.
		/// </summary>
		/// <returns></returns>
		public IEnumerable<PushRequest> FindAll()
		{
			try
			{
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
			return Repository.FindAll();
		}

		/// <summary>
		/// Finds all push requests associated with a specified push application.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <returns></returns>
		public IEnumerable<PushRequest> FindByAppId(string id)
		{
			try
			{
				return Repository.FindByAppId(id);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Finds all push requests by the specified date range.
		/// </summary>
		/// <param name="fromDate">From date.</param>
		/// <param name="toDate">To date.</param>
		/// <param name="startIndex">The start index.</param>
		/// <param name="endIndex">The end index.</param>
		/// <returns></returns>
		public IEnumerable<PushRequest> FindByDateRange(DateTime fromDate, DateTime toDate, int startIndex, int endIndex)
		{
			try
			{
				return Repository.FindByDateRange(fromDate, toDate, startIndex, endIndex);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Finds a push request by the specified id.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <returns></returns>
		public PushRequest FindById(string id)
		{
			try
			{
				return Repository.FindById(id);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Finds all push requests that are incomplete for a given push application.
		/// </summary>
		/// <param name="applicationId">The application id.</param>
		/// <returns></returns>
		public IEnumerable<PushRequest> FindIncomplete(string applicationId)
		{
			try
			{
				return Repository.FindIncomplete(applicationId);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		/// <summary>
		/// Gets size of all push requests by the specified date range.
		/// </summary>
		/// <param name="fromDate">From date.</param>
		/// <param name="toDate">To date.</param>
		/// <returns></returns>
		public int GetSizeByDateRange(DateTime fromDate, DateTime toDate)
		{
			try
			{
				return Repository.GetSizeByDateRange(fromDate, toDate);
			}
			catch (Exception exception)
			{
				throw new PushSDKException("Exception caught creating push request details.", exception);
			}
		}

		#endregion
	}
}
